package microcredit.gui.editor.loan;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.text.JTextComponent;

import microcredit.gui.base.BaseAction;
import microcredit.gui.editor.AbstractEditorForm;
import microcredit.gui.handler.MemberHandler;
import microcredit.gui.selector.IFilter;
import microcredit.gui.table.BeanTable;
import microcredit.gui.table.TableFactory;
import microcredit.gui.util.ExceptionDialog;
import microcredit.gui.util.FormatUtilities;
import microcredit.gui.util.MemberEditorField;
import microcredit.model.Member;
import microcredit.model.MemberToLoan;
import microcredit.model.MemberToLoanEntry;
import microcredit.model.ValidationException;
import microcredit.model.bo.BOLoan;
import microcredit.model.bo.BOMember;
import microcredit.model.domain.LoanStatus;
import microcredit.model.util.ModelUtils;
import net.miginfocom.swing.MigLayout;

public class LoanGeneralForm extends AbstractEditorForm<MemberToLoan> {
	private JScrollPane scrollPane;
	
	private BeanTable<MemberToLoanEntry> beanTable;
	private JLabel lblAmount;
	private JLabel lblReturnedAmount;
	private JTextField returnAmountTextField;
	private JTextField returnedAmountTextField;
	
	private MemberToLoan loan;
	private boolean updating;
	private boolean selectionUpdating;
	private MemberEditorField security1Field;
	private JLabel security1Label;
	private JLabel lblSecurity;
	private JLabel lblSecurity_1;
	private MemberEditorField security2Field;
	private MemberEditorField security3Field;
	private JSeparator securitySeparator;
	private JSeparator separator;
	private JPanel statisticPanel;
	private JLabel outstandingLabel;
	private JTextField outstandingTextField;
	
	
	private final ChangeLoanAction changeLoanAction = new ChangeLoanAction(this);
	private final SecurityRepaidAction securityRepaidAction = new SecurityRepaidAction(this);
	private final CancelledDeadAction cancelledDeadAction = new CancelledDeadAction(this);
	private MToolBar buttonPanel;
	private LoanPanel loanPanel;
	private JSeparator separator_1;
	private LoanExtensionPanel loanExtensionPanel;
	
	private ToggleActionButton securityRepaidToggleButton;
	private ToggleActionButton cancelledDeadToggleButton;
	
	public LoanGeneralForm() {
		super();
		setTitle("General");
		initialize();
		TableFactory.fill(getBeanTable(), MemberToLoanEntry.class);
		
		
		initListeners();
		initActions();
	}
	
	@Override
	public void focusDefault() {
		if (getLoanPanel().isVisible() && getLoanPanel().isEditable()) {
			getLoanPanel().focusDefault();
		} else if (getLoanExtensionPanel().isVisible() && getLoanExtensionPanel().isReasonEditable()) {
			getLoanExtensionPanel().focusDefault(); 
		}
	}
	
	@Override
	public MemberToLoan receive(MemberToLoan element) {
		// apply security members
		Member security1 = getSecurity1Field().getElement();
		Member security2 = getSecurity2Field().getElement();
		Member security3 = getSecurity3Field().getElement();
		List<Member> securities = new ArrayList<Member>();
		if (security1 != null) {
			securities.add(security1);
		}
		if (security2 != null) {
			securities.add(security2);
		}
		if (security3 != null) {
			securities.add(security3);
		}
		element.setSecurity1(securities.size() > 0 ? securities.get(0) : null);
		element.setSecurity2(securities.size() > 1 ? securities.get(1) : null);
		element.setSecurity3(securities.size() > 2 ? securities.get(2) : null);
		
		element = getLoanPanel().receive(element);
		element = getLoanExtensionPanel().receive(element);
		
		return element;
	}

	@Override
	public boolean onSave(MemberToLoan element) throws Throwable {
		boolean superValue = super.onSave(element);
		return superValue;
	}
	
	@Override
	public void afterSave(MemberToLoan element) throws Throwable {
		if (element.areAllEntriesFinalized()
				&& !element.isFinalized()
				&& ModelUtils.compare(element.getReturnAmount(), element.getAmountPaid()) > 0 ) {
			// if all entries are finalized but amount is not paid autoextend the loan
			String message = "The loan is not yet paid, but no more loan rates are available.\n\nDo you want to extend the loan?";
			int result = JOptionPane.showConfirmDialog(this, message);
			if (result == JOptionPane.YES_OPTION) {
				emulateAction(changeLoanAction);
			}
		}
		
	}
	
	@Override
	public void apply(MemberToLoan element) {
		this.loan = element;
		updating = true;
		try {
			getSecurity1Field().setElement(element.getSecurity1());
			getSecurity2Field().setElement(element.getSecurity2());
			getSecurity3Field().setElement(element.getSecurity3());
			
			getLoanPanel().apply(element);
			getLoanExtensionPanel().apply(element);
			
			securityRepaidToggleButton.setSelected(element.getLoanStatus() == LoanStatus.R || element.getLoanStatus() == LoanStatus.K);
			cancelledDeadToggleButton.setSelected(element.getLoanStatus() == LoanStatus.N);
			
			updateEntries(element);
			
			updateState();
		} finally {
			updating = false;
		}

	}
	
	
	private void updateEntries(MemberToLoan element) {
		List<MemberToLoanEntry> entries = element.getMemberToLoanEntries();
		if (entries == null) {
			getBeanTable().load(entries);
		} else {
			getBeanTable().load(entries.subList(0, element.getNumberOfWeeks()));
		}
	}
	
	private void updateAmounts(MemberToLoan element) {
		try {
			BOLoan.create().generateLoanEntries(element);
		} catch (ValidationException e) {
			ExceptionDialog.handleException(this, e);
		}
		getReturnAmountTextField().setText(FormatUtilities.formatMoney(element.getReturnAmount()));
		getReturnedAmountTextField().setText(FormatUtilities.formatMoney(element.getAmountPaid()));
		getOutstandingTextField().setText(FormatUtilities.formatMoney(element.getOutstandingAmount()));
	}
	protected void securityRepaid() throws Exception {
		loan = receive(loan);
		try {
			BOLoan.create().toggleAsSecurityPaid(loan);
		} finally {
			apply(loan);
		}
	}
	
	protected void cancelled() throws Exception {
		loan = receive(loan);
		try {
			BOLoan.create().toggleAsCancelled(loan);
		} finally {
			apply(loan);
		}
	}
	
	protected void extendLoan() throws Exception {
		loan = receive(loan);
		try {
			BOLoan.create().generateExtension(loan);
		} finally {
			apply(loan);
		}
	}
	
	void dataChanged() {
		if (!updating) {
			loan = receive(loan);
			try {
				BOLoan.create().generateLoanEntries(loan);
			} catch (Throwable e) {
				ExceptionDialog.handleException(this, e);
			}
			updateEntries(loan);
		}
	}

	@Override
	public Dimension getDefaultSize() {
		return new Dimension(540, 600);
	}
	
	private void initActions() {
		
		addAction(changeLoanAction);
		addAction(securityRepaidAction);
		addAction(cancelledDeadAction);
		
		getButtonPanel().addAction(changeLoanAction);
		securityRepaidToggleButton = getButtonPanel().addToggleAction(securityRepaidAction);
		cancelledDeadToggleButton = getButtonPanel().addToggleAction(cancelledDeadAction);
	}
	
	private void initListeners() {
		getBeanTable().getBeanModel().addTableModelListener(new TableModelListener() {
			@Override
			public void tableChanged(TableModelEvent e) {
				updateAmounts(receive(loan));
			}
		});
		
		getBeanTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				selectionUpdated();
			}
		});
		getBeanTable().getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				selectionUpdated();
			}
		});
	}
	

	private void selectionUpdated() {
		if (!selectionUpdating) {
			try {
				selectionUpdating = true;
				if (!getBeanTable().hasFocus()) {
					return;
				}
				int row = getBeanTable().getSelectedRow();
				int column = getBeanTable().getSelectedColumn();
				int editingRow = getBeanTable().getEditingRow();
				int editingColumn = getBeanTable().getEditingColumn();
				
				int modelColumn = getBeanTable().convertColumnIndexToModel(column);
				
				if (modelColumn == 4 && row == editingRow && column == editingColumn) {
					return;
				}
				int newRow = row;
				int newColumn = getBeanTable().convertColumnIndexToView(4);
				
				if (newRow == -1 || newColumn == -1) {
					return;
				}
				
				if (getBeanTable().isCellEditable(newRow, newColumn)) {
					getBeanTable().getColumnModel().getSelectionModel().clearSelection();
					getBeanTable().getColumnModel().getSelectionModel().setSelectionInterval(newColumn, newColumn);
//					getBeanTable().editCellAt(newRow, newColumn);
				}
				
//				if (getBeanTable().getEditorComponent() != null) {
//					getBeanTable().getEditorComponent().requestFocus();
//					((JTextComponent)getBeanTable().getEditorComponent()).selectAll();						
//				}
			} finally {
				selectionUpdating = false;
			}
		}
	}
	
	private void initialize() {
		setLayout(new MigLayout("insets 0 8 5 5, hidemode 3", "[right][grow]", "[][][][][][][][][][][][][][][][]"));
		add(getButtonPanel(), "cell 0 1 2 1,grow");
		add(getSecurity1Label(), "cell 0 3");
		add(getSecurity1Field(), "cell 1 3,grow");
		add(getLblSecurity(), "cell 0 4");
		add(getSecurity2Field(), "cell 1 4,grow");
		add(getLblSecurity_1(), "cell 0 5");
		add(getSecurity3Field(), "cell 1 5,grow");
		add(getSecuritySeparator(), "cell 0 6 2 1,grow");
		add(getLoanPanel(), "cell 0 7 2 1,grow");
		add(getSeparator_1(), "cell 0 8 2 1,grow");
		add(getLoanExtensionPanel(), "cell 0 9 2 1,grow");
		add(getSeparator(), "cell 0 10 2 1,grow");
		add(getStatisticPanel(), "cell 0 11 2 1,grow");
		add(getScrollPane(), "cell 0 12 2 1,grow");
	}
	
	private JScrollPane getScrollPane() {
		if (scrollPane == null) {
			scrollPane = new JScrollPane();
			scrollPane.setViewportView(getBeanTable());
			scrollPane.setName("scrollPane");
		}
		return scrollPane;
	}
	public BeanTable<MemberToLoanEntry> getBeanTable() {
		if (beanTable == null) {
			beanTable = new BeanTable<MemberToLoanEntry>();
			beanTable.setName(MemberToLoan.PAYMENT);
			beanTable.setAutoCreateRowSorter(false);
			beanTable.setRowSorter(null);
		}
		return beanTable;
	}
	private JLabel getLblAmount() {
		if (lblAmount == null) {
			lblAmount = new JLabel("To Return:");
		}
		return lblAmount;
	}
	private JLabel getLblReturnedAmount() {
		if (lblReturnedAmount == null) {
			lblReturnedAmount = new JLabel("Returned:");
		}
		return lblReturnedAmount;
	}

	private JTextField getReturnAmountTextField() {
		if (returnAmountTextField == null) {
			returnAmountTextField = new JTextField();
			returnAmountTextField.setBackground(new Color(UIManager.getColor("control").getRGB()));
			returnAmountTextField.setEditable(false);
			returnAmountTextField.setColumns(5);
			returnAmountTextField.setRequestFocusEnabled(false);
		}
		return returnAmountTextField;
	}
	private JTextField getReturnedAmountTextField() {
		if (returnedAmountTextField == null) {
			returnedAmountTextField = new JTextField();
			returnedAmountTextField.setBackground(new Color(UIManager.getColor("control").getRGB()));
			returnedAmountTextField.setEditable(false);
			returnedAmountTextField.setColumns(5);
			returnedAmountTextField.setRequestFocusEnabled(false);
		}
		return returnedAmountTextField;
	}
	private MemberEditorField getSecurity1Field() {
		if (security1Field == null) {
			MemberHandler handler = new RestrictedMemberHandler(this, 0);
			security1Field = new MemberEditorField(null, handler, handler, handler, handler);
			security1Field.setName(MemberToLoan.SECURITY1);
		}
		return security1Field;
	}
	private JLabel getSecurity1Label() {
		if (security1Label == null) {
			security1Label = new JLabel("Security 1:");
		}
		return security1Label;
	}
	private JLabel getLblSecurity() {
		if (lblSecurity == null) {
			lblSecurity = new JLabel("Security 2:");
		}
		return lblSecurity;
	}
	private JLabel getLblSecurity_1() {
		if (lblSecurity_1 == null) {
			lblSecurity_1 = new JLabel("Security 3:");
		}
		return lblSecurity_1;
	}
	private MemberEditorField getSecurity2Field() {
		if (security2Field == null) {
			MemberHandler handler = new RestrictedMemberHandler(this, 1);
			security2Field = new MemberEditorField(null, handler, handler, handler, handler);
		}
		return security2Field;
	}
	private MemberEditorField getSecurity3Field() {
		if (security3Field == null) {
			MemberHandler handler = new RestrictedMemberHandler(this, 2);
			security3Field = new MemberEditorField(null, handler, handler, handler, handler);
		}
		return security3Field;
	}
	private JSeparator getSecuritySeparator() {
		if (securitySeparator == null) {
			securitySeparator = new JSeparator();
		}
		return securitySeparator;
	}
	
	
	private class RestrictedMemberHandler extends MemberHandler {

		private final int index;
		
		public RestrictedMemberHandler(Component parent, int index) {
			super(parent);
			this.index = index;
		}
		
		@Override
		public List<IFilter<Member>> getFilters() {
			List<IFilter<Member>> filters = super.getFilters();
			for (IFilter<Member> iFilter : filters) {
				if (iFilter instanceof MemberGroupFilter) {
					if (loan != null && loan.getMember() != null && loan.getMember().getGroup() != null) {
						((MemberGroupFilter) iFilter).setSelectedValue(loan.getMember().getGroup());
					}
				}
			}
			return filters;
		}
		
		private List<Member> getMembers() {
			List<Member> members = new ArrayList<Member>();
			members.add(getSecurity1Field().getElement());
			members.add(getSecurity2Field().getElement());
			members.add(getSecurity3Field().getElement());
			return members;
		}
		
		@Override
		public List<Member> load() throws Throwable {
			List<Member> readMembers = BOMember.create().readActiveMembers();
			List<Member> alreadySelectedMembers = getMembers();
			int index = 0;
			for (Member member : alreadySelectedMembers) {
				if (index != this.index && member != null) {
					readMembers.remove(member);
				}
				index++;
			}
			return readMembers;
		}
	}
	
	private JSeparator getSeparator() {
		if (separator == null) {
			separator = new JSeparator();
		}
		return separator;
	}
	private JPanel getStatisticPanel() {
		if (statisticPanel == null) {
			statisticPanel = new JPanel();
			statisticPanel.setLayout(new MigLayout("insets 0", "[][][][][][][][]0[grow,right]", "[]"));
			statisticPanel.add(getLblAmount(), "cell 1 0");
			statisticPanel.add(getReturnAmountTextField(), "flowx,cell 3 0");
			statisticPanel.add(getLblReturnedAmount(), "cell 4 0");
			statisticPanel.add(getReturnedAmountTextField(), "cell 5 0");
			statisticPanel.add(getOutstandingLabel(), "cell 6 0,alignx trailing");
			statisticPanel.add(getOutstandingTextField(), "cell 7 0,growx");
		}
		return statisticPanel;
	}
	private JLabel getOutstandingLabel() {
		if (outstandingLabel == null) {
			outstandingLabel = new JLabel("Outstanding:");
		}
		return outstandingLabel;
	}
	private JTextField getOutstandingTextField() {
		if (outstandingTextField == null) {
			outstandingTextField = new JTextField();
			outstandingTextField.setEditable(false);
			outstandingTextField.setColumns(5);
			outstandingTextField.setBackground(new Color(UIManager.getColor("control").getRGB()));
		}
		return outstandingTextField;
	}
	
	private class ChangeLoanAction extends BaseAction{

		public ChangeLoanAction(Component parent) {
			super(parent);
			setName("Extend loan");
			setIcon("x16/add2.png");
		}

		@Override
		public void perform(ActionEvent e) throws Throwable {
			extendLoan();
		}
		
		@Override
		public void updateState() throws Throwable {
			setEnabled(!loan.hasNewExtension() && loan.isEntryFinalized() && !loan.isFinalized());
		}
		
	}
	
	private class SecurityRepaidAction extends BaseAction{

		public SecurityRepaidAction(Component parent) {
			super(parent);
			setName("Security repaid");
			setIcon("x16/pin_yellow.png");
		}

		@Override
		public void perform(ActionEvent e) throws Throwable {
			securityRepaid();
		}
		
		@Override
		public void updateState() throws Throwable {
			setEnabled(!loan.isFinalized() || loan.getLoanStatus() == LoanStatus.R);
		}
	}
	
	private class CancelledDeadAction extends BaseAction{
		public CancelledDeadAction(Component parent) {
			super(parent);
			setName("Cancelled not repaid (dead)");
			setIcon("x16/pin_red.png");
		}

		@Override
		public void perform(ActionEvent e) throws Throwable {
			cancelled();
		}
		
		@Override
		public void updateState() throws Throwable {
			setEnabled(!loan.isFinalized()|| loan.getLoanStatus() == LoanStatus.N);
		}
	}
	
	private MToolBar getButtonPanel() {
		if (buttonPanel == null) {
			buttonPanel = new MToolBar();
			buttonPanel.setInsetsEnabled(false);
		}
		return buttonPanel;
	}
	private LoanPanel getLoanPanel() {
		if (loanPanel == null) {
			loanPanel = new LoanPanel(this);
		}
		return loanPanel;
	}
	private JSeparator getSeparator_1() {
		if (separator_1 == null) {
			separator_1 = new JSeparator();
		}
		return separator_1;
	}
	private LoanExtensionPanel getLoanExtensionPanel() {
		if (loanExtensionPanel == null) {
			loanExtensionPanel = new LoanExtensionPanel(this);
		}
		return loanExtensionPanel;
	}
}
